/**
 *  Copyright 2013 Nate Dierk
 *
 *  COMP 590 -- Fall 2013
 */

#include "GContext.h"
#include "GBitmap.h"
#include "GColor.h"
#include "GIRect.h"

///////////////////////////////////////////////////////////
// GContext0 Definition

class GContext0 : public GContext {
private:
  static inline void BlendPixels(int w, int h, int rowBytes, char* dst, const GPixel& src, BlendType blend){
    int x, y;
    for (y = 0; y < h; ++y){
        for (x = 0; x < w; ++x){
          GPixel_Blend( &((GPixel*) dst)[x], src, blend);
        }
        dst += rowBytes;
      }
  }

protected:
  GBitmap fBmp;

public:
    GContext0(const GBitmap& bmp) {
      fBmp = bmp;
    }
    virtual ~GContext0() {}

    // *  Return the information about the context's bitmap
    void getBitmap(GBitmap* bmp) const {
      *bmp = fBmp;
    }

  // *  Set the entire context's pixels to the specified value.
  void clear(const GColor& color){
    char* row = (char*) fBmp.fPixels;    

    BlendPixels(fBmp.fWidth, fBmp.fHeight,
                fBmp.fRowBytes, row,
                GPixel_PackColor(color), SET);
  };

  void fillIRect(const GIRect& rect, const GColor& color){
    GIRect clipped = rect.clipToBmp(fBmp);

    if (!clipped.isEmpty()){ 
      char* row = ((char*) (fBmp.fPixels + clipped.fLeft)) + // compute pointer to top left pixel
        (fBmp.fRowBytes * clipped.fTop); // add vertical offset
    
      BlendPixels(clipped.width(), clipped.height(), 
                  fBmp.fRowBytes, row, 
                  GPixel_PackColor(color), SRC_OVER);
    }
  };
};

///////////////////////////////////////////////////////////
// Specialized GContext Implementations

class GContext_bmp : public GContext0 {
public:
  GContext_bmp(const GBitmap& bmp) : GContext0(bmp) { }
  ~GContext_bmp(){  }
};

class GContext_hw : public GContext0 {
public:  
  GContext_hw(const GBitmap& bmp) : GContext0(bmp) {}
  ~GContext_hw(){
    free(fBmp.fPixels); 
  }
};

///////////////////////////////////////////////////////////
// Context Validation Helpers
  
static bool isBmpValid(const GBitmap& bmp){
  return  !(bmp.fHeight   <= 0 || 
	    bmp.fWidth    <= 0 || 
	    bmp.fRowBytes < (bmp.fWidth * sizeof(GPixel)));
}

static bool isMemValid(const GBitmap& bmp){
  return bmp.fPixels != NULL;
}

///////////////////////////////////////////////////////////
// GContext Factories

GContext* GContext::Create(const GBitmap& bmp){
  if (isBmpValid(bmp) && isMemValid(bmp)){
    return new GContext_bmp(bmp);
  } else {
    return NULL;
  }
}

GContext* GContext::Create(int width, int height){
  GBitmap bmp;
  bmp.fWidth = width;
  bmp.fHeight = height;
  bmp.fRowBytes = width * sizeof(GPixel);

  if (isBmpValid(bmp)){
    bmp.fPixels = (GPixel*) malloc(height * bmp.fRowBytes);
    
    if (isMemValid(bmp)){
      const GBitmap& pBmp = bmp;
      return new GContext_hw(pBmp);
    }
  } else {
    return NULL;
  }
}


